/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "session_stub.h"
#include "common_utils.h"
#include "isecure_element_channel.h"
#include "loghelper.h"
#include "message_parcel.h"
#include "se_common_exception.h"
#include "se_service_ipc_interface_code.h"
#include "se_hisysevent.h"
#include "se_hianalytics.h"

using namespace std;
namespace OHOS {
namespace SE {
namespace OMAPI {
int SessionStub::OnRemoteRequest(uint32_t code,         /* [in] */
                                 MessageParcel &data,   /* [in] */
                                 MessageParcel &reply,  /* [out] */
                                 MessageOption &option) /* [in] */
{
    InfoLog("SessionStub::OnRemoteRequest occur, code is %{public}d", code);
    if (data.ReadInterfaceToken() != GetDescriptor()) {
        ErrorLog("SessionStub OnRemoteRequest GetDescriptor failed");
        return ErrorCode::SE_IPC_TOKEN_ERROR;
    }
    switch (code) {
        case static_cast<uint32_t>(SeServiceIpcInterfaceCode::COMMAND_SESSION_GET_ATR):
            return HandleGetATR(data, reply);
        case static_cast<uint32_t>(SeServiceIpcInterfaceCode::COMMAND_SESSION_CLOSE):
            return HandleClose(data, reply);
        case static_cast<uint32_t>(SeServiceIpcInterfaceCode::COMMAND_SESSION_IS_CLOSED):
            return HandleIsClosed(data, reply);
        case static_cast<uint32_t>(SeServiceIpcInterfaceCode::COMMAND_SESSION_CLOSE_CHANNELS):
            return HandleCloseChannels(data, reply);
        case static_cast<uint32_t>(SeServiceIpcInterfaceCode::COMMAND_SESSION_OPEN_BASIC_CHANNEL):
            return HandleOpenBasicChannel(data, reply);
        case static_cast<uint32_t>(SeServiceIpcInterfaceCode::COMMAND_SESSION_OPEN_LOGIC_CHANNEL):
            return HandleOpenLogicalChannel(data, reply);
        default:
            return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
    }
}

int SessionStub::HandleGetATR(OHOS::MessageParcel& data, OHOS::MessageParcel& reply)
{
    // Note: don't check the IPC permssion, secure element security is by Access Rule.
    std::string response;
    int statusCode = GetATR(response);
    reply.WriteString(response);
    reply.WriteInt32(statusCode);
    return ERR_NONE;
}

int SessionStub::HandleClose(OHOS::MessageParcel& data, OHOS::MessageParcel& reply)
{
    // Note: don't check the IPC permssion, secure element security is by Access Rule.
    int statusCode = Close();
    reply.WriteInt32(statusCode);
    return ERR_NONE;
}

int SessionStub::HandleIsClosed(OHOS::MessageParcel& data, OHOS::MessageParcel& reply)
{
    // Note: don't check the IPC permssion, secure element security is by Access Rule.
    bool isClosed = false;
    int statusCode = IsClosed(isClosed);
    reply.WriteBool(isClosed);
    reply.WriteInt32(statusCode);
    return ERR_NONE;
}

int SessionStub::HandleCloseChannels(OHOS::MessageParcel& data, OHOS::MessageParcel& reply)
{
    // Note: don't check the IPC permssion, secure element security is by Access Rule.
    int statusCode = CloseChannels();
    reply.WriteInt32(statusCode);
    return ERR_NONE;
}

int SessionStub::HandleOpenBasicChannel(OHOS::MessageParcel& data, OHOS::MessageParcel& reply)
{
    // Note: don't check the IPC permssion, secure element security is by Access Rule.
    std::string aid = data.ReadString();
    uint8_t p2 = static_cast<uint8_t>(data.ReadInt32());
    InfoLog("SessionStub::HandleOpenBasicChannel aid = %{public}s, p2 = %{public}d", aid.c_str(), p2);
    int32_t status = ErrorCode::SE_SUCCESS;
    std::string apduResp = "";
    try {
        sptr<ISecureElementChannel> basicChannel;
        status = OpenBasicChannel(aid, p2, basicChannel, apduResp);
        std::transform(apduResp.begin(), apduResp.end(), apduResp.begin(), ::toupper);
        if (basicChannel) {
            // open channel success, record 1 open count and 0 fail count.
            SeHisysEvent::WriteOpenChannelHiSysEvent(DEFAULT_COUNT, NOT_COUNT);
            reply.WriteRemoteObject(basicChannel->AsObject());
        } else {
            if (IsApduResponseUnexpected(apduResp)) {
                // open channel success, but apdu apduResp is unexpected.
                WarnLog("HandleOpenBasicChannel Channel apduResp is unexpect");
            } else {
                // open channel failed, record failed events.
                SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, ErrorCode::SE_GENERAL_ERROR);
                ErrorLog("HandleOpenBasicChannel failed: basicChannel null");
            }
        }
    // when error caught, record related failed reason.
    } catch (const AccessControlError& e) {
        ErrorLog("HandleOpenBasicChannel failed: AccessControlError");
        status = ErrorCode::SE_ACCESS_CONTROL_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const IllegalStateError& e) {
        ErrorLog("HandleOpenBasicChannel failed: IllegalStateError");
        status = ErrorCode::SE_ILLEGAL_STATE_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const UnsupportedOperationError& e) {
        ErrorLog("HandleOpenBasicChannel failed: UnsupportedOperationError");
        status = ErrorCode::SE_OPERATION_NOT_SUPPORTED_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const IllegalArgumentError& e) {
        ErrorLog("HandleOpenBasicChannel failed: IllegalArgumentError");
        status = ErrorCode::SE_ILLEGAL_PARAMETER_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const IllegalReferenceError& e) {
        ErrorLog("HandleOpenBasicChannel failed: IllegalReferenceError");
        status = ErrorCode::SE_ILLEGAL_REFERENCE_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const NoSuchElementError& e) {
        ErrorLog("HandleOpenBasicChannel failed: NoSuchElementError");
        status = ErrorCode::SE_NO_SUCH_ELEMENT_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const ChannelNotAvailableError& e) {
        ErrorLog("HandleOpenBasicChannel failed: ChannelNotAvailableError");
        status = ErrorCode::SE_CHANNEL_NOT_AVAILABLE_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const SecurityError& e) {
        ErrorLog("HandleOpenBasicChannel failed: IOError");
        status = ErrorCode::SE_IO_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
        SeHiAnalytics::GetInstance().SeEventLog(LOG_CHANNEL_IOEXCEPTION, LOG_DEFAULT_STR);
    } catch (const IOError& e) {
        ErrorLog("HandleOpenBasicChannel failed: IOError");
        status = ErrorCode::SE_IO_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
        SeHiAnalytics::GetInstance().SeEventLog(LOG_CHANNEL_IOEXCEPTION, LOG_DEFAULT_STR);
    } catch (const GeneralError& e) {
        ErrorLog("HandleOpenBasicChannel failed: GeneralError");
        status = ErrorCode::SE_GENERAL_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    } catch (const NullPointerError& e) {
        ErrorLog("HandleOpenBasicChannel failed: NullPointerError");
        status = ErrorCode::SE_GENERAL_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::BASIC_CHANNEL, (ErrorCode)status);
    }
    reply.WriteInt32(status);
    reply.WriteString(apduResp);
    return ERR_NONE;
}

int SessionStub::HandleOpenLogicalChannel(OHOS::MessageParcel& data, OHOS::MessageParcel& reply)
{
    // Note: don't check the IPC permssion, secure element security is by Access Rule.
    std::string aid = data.ReadString();
    uint8_t p2 = static_cast<uint8_t>(data.ReadInt32());
    InfoLog("SessionStub::HandleOpenLogicalChannel aid = %{public}s, p2 = %{public}d", aid.c_str(), p2);
    int32_t status = ErrorCode::SE_SUCCESS;
    std::string apduResp = "";
    try {
        sptr<ISecureElementChannel> logicChannel;
        status = OpenLogicalChannel(aid, p2, logicChannel, apduResp);
        std::transform(apduResp.begin(), apduResp.end(), apduResp.begin(), ::toupper);
        if (logicChannel) {
            // open channel success, record 1 open count and 0 fail count.
            SeHisysEvent::WriteOpenChannelHiSysEvent(DEFAULT_COUNT, NOT_COUNT);
            reply.WriteRemoteObject(logicChannel->AsObject());
        } else {
            if (IsApduResponseUnexpected(apduResp)) {
                // open channel success, but apdu apduResp is unexpected.
                WarnLog("OpenLogicalChannel Channel apduResp is unexpect");
            } else {
                // open channel failed, record 1 open count and 1 fail count, and record failed events.
                SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, ErrorCode::SE_GENERAL_ERROR);
                ErrorLog("OpenLogicalChannel failed: logicChannel null");
            }
        }
    // when error caught, record related failed reason and statistic events.
    } catch (const AccessControlError& e) {
        ErrorLog("OpenLogicalChannel failed: AccessControlError");
        status = ErrorCode::SE_ACCESS_CONTROL_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const IllegalStateError& e) {
        ErrorLog("OpenLogicalChannel failed: IllegalStateError");
        status = ErrorCode::SE_ILLEGAL_STATE_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const UnsupportedOperationError& e) {
        ErrorLog("OpenLogicalChannel failed: UnsupportedOperationError");
        status = ErrorCode::SE_OPERATION_NOT_SUPPORTED_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const IllegalArgumentError& e) {
        ErrorLog("OpenLogicalChannel failed: IllegalArgumentError");
        status = ErrorCode::SE_ILLEGAL_PARAMETER_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const IllegalReferenceError& e) {
        ErrorLog("OpenLogicalChannel failed: IllegalReferenceError");
        status = ErrorCode::SE_ILLEGAL_REFERENCE_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const NoSuchElementError& e) {
        ErrorLog("OpenLogicalChannel failed: NoSuchElementError");
        status = ErrorCode::SE_NO_SUCH_ELEMENT_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const ChannelNotAvailableError& e) {
        ErrorLog("OpenLogicalChannel failed: ChannelNotAvailableError");
        status = ErrorCode::SE_CHANNEL_NOT_AVAILABLE_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const SecurityError& e) {
        ErrorLog("OpenLogicalChannel failed: IOError");
        status = ErrorCode::SE_IO_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
        SeHiAnalytics::GetInstance().SeEventLog(LOG_CHANNEL_IOEXCEPTION, LOG_DEFAULT_STR);
    } catch (const IOError& e) {
        ErrorLog("OpenLogicalChannel failed: IOError");
        status = ErrorCode::SE_IO_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
        SeHiAnalytics::GetInstance().SeEventLog(LOG_CHANNEL_IOEXCEPTION, LOG_DEFAULT_STR);
    } catch (const GeneralError& e) {
        ErrorLog("OpenLogicalChannel failed: GeneralError");
        status = ErrorCode::SE_GENERAL_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    } catch (const NullPointerError& e) {
        ErrorLog("OpenLogicalChannel failed: NullPointerError");
        status = ErrorCode::SE_GENERAL_ERROR;
        SeHisysEvent::WriteSeFailedHiSysEvent(ChannelType::LOGIC_CHANNEL, (ErrorCode)status);
    }
    reply.WriteInt32(status);
    reply.WriteString(apduResp);
    return ERR_NONE;
}

bool SessionStub::IsApduResponseUnexpected(const std::string& response)
{
    InfoLog("IsApduResponseUnexpected response len = %{public}lu", response.size());
    if (response.empty()) {
        WarnLog("response is empty");
        return false;
    }
    // apdu sw length
    constexpr int SW_LENGTH = 4;
    if (response.size() >= SW_LENGTH) {
        const std::string aidNotFound = "6A82";

        // open channel success, but SW is 6A82, means aid is not present
        if (response.substr(response.length() - SW_LENGTH, SW_LENGTH) == aidNotFound) {
            InfoLog("IsApduResponseUnexpected responseSW is 6A82");
            return true;
        }
    }

    return false;
}
}  // namespace OMAPI
}  // namespace SE
}  // namespace OHOS